(function($) {
	jQuery.fn.html5_upload = function(options) {
		if(options == "destroy"){
			$(this).trigger('html5_upload.destroy');
			return this;
		}

		var available_events = ['onStart', 'onStartOne', 'onProgress', 'onFinishOne', 'onFinish', 'onError'];
		var options = jQuery.extend({
onStart: function(event, total) {
				return true;
			},
onStartOne: function(event, name, number, total) {
				return true;
			},
onProgress: function(event, progress, name, number, total) {
			},
onFinishOne: function(event, response, name, number, total) {
			},
onFinish: function(event, total) {
			},
onError: function(event, name, error) {
			},
onBrowserIncompatible: function() {
				alert("Sorry, but your browser is incompatible with uploading files using HTML5 (at least, with current preferences.\n Please install the latest version of Firefox, Safari or Chrome");
			},
autostart: true,
autoclear: true,
stopOnFirstError: false,
sendBoundary: false,
fieldName: 'user_file[]',//ignore if sendBoundary is false
method: 'post',

STATUSES: {
				'STARTED':		'Started',
				'PROGRESS':		'Progress',
				'LOADED':		'Loaded',
				'FINISHED':		'FInished'
			},
headers: {
				"Cache-Control":"no-cache",
				"X-Requested-With":"XMLHttpRequest",
				"X-File-Name": function(file){return file.fileName},
				"X-File-Size": function(file){return file.fileSize},
				"Content-Type": function(file){
					if (!options.sendBoundary) return 'multipart/form-data';
					return false;
				}
			},
setName: function(text) {},
setStatus: function(text) {},
setProgress: function(value) {},

genName: function(file, number, total) {
				return file + " (" + (number+1) + " / " + total + ")";
			},
genStatus: function(progress, finished) {
				if (finished) {
					return options.STATUSES['FINISHED'];
				}
				if (progress == 0) {
					return options.STATUSES['STARTED'];
				}
				else if (progress == 1) {
					return options.STATUSES['LOADED'];
				}
				else {
					return options.STATUSES['PROGRESS'];
				}
			},
genProgress: function(loaded, total) {
				return loaded / total;
			}
		}, options);

		function onDrop(evt) {
				evt.stopPropagation();
				evt.preventDefault();
				this.files = evt.dataTransfer.files;

				this.count = this.files.length;

				if (options.autostart) {
					$(this).bind('change', upload);
					$(this).change();
				}
				
			}
			
		function upload() {
			var files = this.files;
			var total = files.length;
			var $this = $(this);
			if (!$this.triggerHandler('html5_upload.onStart', [total])) {
				return false;
			}
			this.disabled = true;
			var uploaded = 0;
			var xhr = this.html5_upload['xhr'];
			this.html5_upload['continue_after_abort'] = true;
			function upload_file(number) {
				if (number == total) {
					$this.trigger('html5_upload.onFinish', [total]);
					options.setStatus(options.genStatus(1, true));
					$this.attr("disabled", false);
					if (options.autoclear) {
						$this.val("");
					}
					return;
				}
				var file = files[number];
				if (!$this.triggerHandler('html5_upload.onStartOne', [file.fileName, number, total])) {
					return upload_file(number+1);
				}
				options.setStatus(options.genStatus(0));
				options.setName(options.genName(file.fileName, number, total));
				options.setProgress(options.genProgress(0, file.fileSize));
				xhr.upload['onprogress'] = function(rpe) {
					$this.trigger('html5_upload.onProgress', [rpe.loaded / rpe.total, file.fileName, number, total]);
					options.setStatus(options.genStatus(rpe.loaded / rpe.total));
					options.setProgress(options.genProgress(rpe.loaded, rpe.total));
				};
				xhr.onload = function(load) {
					$this.trigger('html5_upload.onFinishOne', [xhr.responseText, file.fileName, number, total]);
					options.setStatus(options.genStatus(1, true));
					options.setProgress(options.genProgress(file.fileSize, file.fileSize));
					upload_file(number+1);
				};
				xhr.onabort = function() {
					if ($this[0].html5_upload['continue_after_abort']) {
						upload_file(number+1);
					}
					else {
						$this.attr("disabled", false);
						if (options.autoclear) {
							$this.val("");
						}
					}
				};
				xhr.onerror = function(e) {
					$this.trigger('html5_upload.onError', [file.fileName, e]);
					if (!options.stopOnFirstError) {
						upload_file(number+1);
					}
				};
				xhr.open(options.method, typeof(options.url) == "function" ? options.url(number) : options.url, true);
				$.each(options.headers,function(key,val){
					val = typeof(val) == "function" ? val(file) : val; // resolve value
					if (val === false) return true; // if resolved value is boolean false, do not send this header
					xhr.setRequestHeader(key, val);
				});

				if (!options.sendBoundary) {
					xhr.send(file);
				}
				else {
					if (window.FormData) {//Many thanks to scottt.tw
						var f = new FormData();
						f.append(typeof(options.fieldName) == "function" ? options.fieldName() : options.fieldName, file);
						xhr.send(f);
					}
					else if (file.getAsBinary) {//Thanks to jm.schelcher
						var boundary = '------multipartformboundary' + (new Date).getTime();
						var dashdash = '--';
						var crlf     = '\r\n';

						/* Build RFC2388 string. */
						var builder = '';

						builder += dashdash;
						builder += boundary;
						builder += crlf;

						builder += 'Content-Disposition: form-data; name="'+(typeof(options.fieldName) == "function" ? options.fieldName() : options.fieldName)+'"';

						//thanks to oyejo...@gmail.com for this fix
						fileName = unescape(encodeURIComponent(file.fileName)); //encode_utf8

						builder += '; filename="' + fileName + '"';
						builder += crlf;

						builder += 'Content-Type: application/octet-stream';
						builder += crlf;
						builder += crlf;

						/* Append binary data. */
						builder += file.getAsBinary();
						builder += crlf;

						/* Write boundary. */
						builder += dashdash;
						builder += boundary;
						builder += dashdash;
						builder += crlf;
						xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
						xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
						xhr.sendAsBinary(builder);
					}
					else {
						options.onBrowserIncompatible();
					}
				}
			}
			upload_file(0);
			return true;
		}
		function onDragEnter(evt) {
				evt.stopPropagation();
				evt.preventDefault();
			}
		function OnDragOver(evt){
				evt.stopPropagation();
				evt.preventDefault();
			}
		function onDragExit(evt){
				evt.stopPropagation();
				evt.preventDefault();
		}
		return this.each(function() {
			this.addEventListener("dragenter", onDragEnter , false);
			this.addEventListener("dragexit", onDragExit, false);
			this.addEventListener("dragover",OnDragOver, false);
			this.addEventListener("drop", onDrop, false);

			this.html5_upload = {
xhr:					new XMLHttpRequest(),
				continue_after_abort:	true
			};
			for (event in available_events) {
				if (options[available_events[event]]) {
					$(this).bind("html5_upload."+available_events[event], options[available_events[event]]);
				}
			}
			$(this)
			.bind('html5_upload.start', upload)
			.bind('html5_upload.cancelOne', function() {
				this.html5_upload['xhr'].abort();
			})
			.bind('html5_upload.cancelAll', function() {
				this.html5_upload['continue_after_abort'] = false;
				this.html5_upload['xhr'].abort();
			})
			.bind('html5_upload.destroy', function() {
				this.html5_upload['continue_after_abort'] = false;
				//this.xhr.abort();
				delete this.html5_upload;
				this.removeEventListener("dragenter", onDragEnter , false);
				this.removeEventListener("dragexit", onDragExit, false);
				this.removeEventListener("dragover",OnDragOver, false);
				this.removeEventListener("drop", onDrop, false);
				
				$(this).unbind('html5_upload.*').unbind('change', upload);
			});
		});
	};
})(jQuery);
